using System;
using System.ComponentModel;
using System.Windows.Forms;
using ShadeTree.Binding.Validation;

namespace ShadeTree.Binding.Elements
{
    public abstract class TextEditingElement<CONTROLTYPE> : BoundScreenElement<CONTROLTYPE, object>, ITextEditingElement
        where CONTROLTYPE : Control
    {
        private CoerceFunction _coercion;
        private FormatValue _format;

        public TextEditingElement(Accessor property, CONTROLTYPE control) : base(property, control)
        {
            setupEvents(control);

            if (CoercionUtility.IsNumeric(property.PropertyType))
            {
                setTheAlignmentToTheRight(control);
            }

            if (property.PropertyType == typeof (string))
            {
                int length = MaximumStringLengthAttribute.GetLength(property.InnerProperty);
                setTheMaximumLength(length);
            }

            _coercion = ((p, rawValue) => CoercionUtility.Coerce(p, rawValue));
            _format = (o => o.ToString());
        }

        protected void setupEvents(CONTROLTYPE control)
        {
            // Changed this event from validating to LostFocus because the validating
            // event prevents the GUI from transitioning from View Mode to Edit Mode 6/24/07
            control.Validated += editingComplete;
            control.KeyDown += control_KeyDown;
        }

        #region ITextEditingElement Members

        public CoerceFunction Coercion
        {
            get { return _coercion; }
            set { _coercion = value; }
        }

        public FormatValue Format
        {
            get { return _format; }
            set { _format = value; }
        }

        #endregion

        protected abstract void setTheMaximumLength(int length);

        protected override void tearDown()
        {
            BoundControl.Validated -= editingComplete;
            BoundControl.KeyDown -= control_KeyDown;
        }

        protected abstract void setTheAlignmentToTheRight(CONTROLTYPE control);

        private void control_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                var args = new CancelEventArgs(false);
                fireChanges(args);
            }
        }

        protected void editingComplete(object sender, EventArgs e)
        {
            var ce = new CancelEventArgs(false);
            fireChanges(ce);
        }

        protected void fireChanges(CancelEventArgs e)
        {
            try
            {
                elementValueChanged();
            }
            catch (Exception)
            {
                e.Cancel = true;
                // TODO -- data does not parse
                //SetError(Notification.INVALID_FORMAT);
            }
        }

        protected override object getValueFromControl()
        {
            if (string.IsNullOrEmpty(getText()))
            {
                return null;
            }

            object convertedValue = convertValue();
            return convertedValue;
        }

        protected object convertValue()
        {
            return _coercion(accessor, getText());
        }

        protected override void resetControl(object originalValue)
        {
            string text = originalValue == null ? string.Empty : _format(originalValue);
            setText(text);
        }

        protected virtual string getText()
        {
            return BoundControl.Text;
        }

        protected virtual void setText(string text)
        {
            BoundControl.Text = text;
        }
    }
}